/*
 * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2009.
 *
 * Licensed under the Aduna BSD-style license.
 */

package edu.tju.commons.collections.iterators;

import java.util.Arrays;
import java.util.Iterator;

/**
 * An iterator that returns the bag union of the results of a number of
 * iterators. Note that the result of the iterator is a bag, not a set. The
 * UnionIterator does not do any duplicate filtering.
 */
public class UnionIterator<E> extends LookAheadIterator<E> {

	private final Iterator<? extends Iterator<? extends E>> argIter;

	private Iterator<? extends E> currentIter;

	/**
	 * Creates a new UnionIterator that returns the bag union of the results of a
	 * number of iterators.
	 * 
	 * @param args
	 *        The iterators containing the elements to iterate over.
	 */
	public UnionIterator(Iterator<? extends E>... args) {
		this(Arrays.asList(args));
	}

	/**
	 * Creates a new UnionIterator that returns the bag union of the results of a
	 * number of iterators.
	 * 
	 * @param args
	 *        The iterators containing the elements to iterate over.
	 */
	public UnionIterator(Iterable<? extends Iterator<? extends E>> args) {
		argIter = args.iterator();

		// Initialize with empty iterator so that var is never null
		currentIter = new EmptyIterator<E>();
	}

	protected E getNextElement() {
		if (currentIter.hasNext()) {
			return currentIter.next();
		}
		else {
			// Current iterator exhausted, continue with the next one
			Iterators.closeCloseable(currentIter);

			synchronized (this) {
				if (argIter.hasNext()) {
					currentIter = argIter.next();
					return getNextElement();
				}
				else {
					// All elements have been returned
					return null;
				}
			}
		}
	}

	@Override
	protected void handleClose() {
		super.handleClose();

		synchronized (this) {
			while (argIter.hasNext()) {
				Iterators.closeCloseable(argIter.next());
			}
		}

		Iterators.closeCloseable(currentIter);
	}
}
